// ==================================================================================
// Shared Genomics Project MPI Codebase
// Version 1.0 30/04/2010
//
// (c) 2010 University of Manchester all rights reserved
//
// This file is distributed under the GNU General Public License, Version 2.  
// Please see the file COPYING.txt for more details
// ==================================================================================

#ifndef _UTILS_H_
#define _UTILS_H_

#include <stdio.h>
#include "types.h"

/*!
 * \file
 * \ingroup pmaths
 * \brief	'C' Utility Functions for memory management.
 * \details 'C' utility functions, mainly simple memory management and assignment routines.
 *          Lot's of existing API's do provide this functionality found simpler to write the code
 *          from scratch.
 */

#ifdef __cplusplus
extern "C" {
#endif

/*!
* \brief Coded value for a missing level written to a phenotype file containing a response variable to analyse.
*/
#define MISSING_LEVEL "-9"

/*! 
* \brief	Create an Array of specified size.
* \details	Create an array of specified element size.
*           This method general not used as have to cast the VOID to access the 
*			array elements in practise.
* \param[out] dst Pointer to assign the memory to.	
* \param[in] datatype_size Element size in bytes (e.g. sizeof(int)=4)
* \param[in] nElements Array size to assign
* \return an integer representing 'TRUE' if successful.
* \deprecated This method general not used as have to cast the VOID to access the array elements.
*/
int create_array(void** dst, int datatype_size, int nElements);

/*!
* \brief	Write message to STDERR, PLINK utility function.
* \param[in] message The text of the message
* \return an integer representing 'TRUE' if successful.
*/
int error(const char *message);

/*!
* \brief	Write message to STDOUT.
* \details	Message was written to STDOUT and a local log file.
*			When code went parrallel, STDOUT write sufficient for basic message display.
* \param[in] message The text of the message
* \return an integer representing 'TRUE' if successful.
*/
int print_log(const char* message);

/*!
* \brief	Read a string from a text file I/O handle.
* \details	A string is defined as any contiguous sequence of alphanumeric/punctuation characters.<br>
*			Strings on a line of text delimited by any sequence of whitespace characters.<br>
*           The string is NULL-terminated after reading.<br>
*           The length of each string should not exceed the buffer length 
*           as this function does no bounds checking.<br>
* \param[in] fin Text input stream (fails if NULL)
* \param[in] buffer A buffer to place the string into (fails if NULL)
* \param[in] buffer_length Length of the buffer (fails if <= 0)
* \return 'TRUE' if successful.
*/
BOOL read_string(FILE *fin, char *buffer, int buffer_length);

/*! 
* \brief Create BOOL array of specified size and value.
* \details Deallocate the array with a call to free().
* \param[in] size Length of the array (fails if <= 0)
* \param[in] initial_value (ideally TRUE or FALSE but can be any 'char' value).
* \return BOOL array of specified size (return NULL on failure)
*/
BOOL* create_bool_array(int size, BOOL initial_value);

/*! 
* \brief Create vector of specified size and value.
* \details Deallocate the array with a call to free().
* \param[in] size Length of the array (fails if <= 0)
* \param[in] initial_value
* \return VECTOR_T of specified size (return NULL on failure)
*/
VECTOR_T create_vector(int size, double initial_value);

/*! 
* \brief long int array of specified size and value.
* \details Deallocate the array with a call to free().
*		   In 64-bit platforms, for VC++, long integer = integer
* \param[in] rows No. rows of the array
* \param[in] max_width No. columns of the array
* \param[in] initial_value Initialisation value
* \return long int array of specified size (return NULL on failure)
*/
long int** create_2d_long_int_array(int rows, int max_width, long int initial_value);

/*!
* \brief Create int array of specified size and value.
* \details Deallocate the array with a call to free().
* \param[in] size Length of the array (fails if <= 0)
* \param[in] initial_value
* \return int array of specified size (return NULL on failure)
*/
int* create_int_array(int size, int initial_value);

/*!
* \brief Create a matrix of specified dimensions
* \details Deallocate the array with a call to free_matrix()
* \param[in] rows Number of rows in the matrix (fails if <= 0)
* \param[in] columns Number of columns in the matrix (fails if <= 0)
* \param[in] initial_value Start value for the elements of the matrix.
* \return MATRIX_T (return NULL on failure)
* \see free_matrix()
*/
MATRIX_T create_matrix(int rows, int columns, double initial_value);

/*!
* \brief %Set all elements of a vector to zero.
* \param[in] v Vector to initialise
* \param[in] cols Number of columns/elements in the VECTOR_T
*/
void zero_vector(VECTOR_T v, int cols);

/*!
* \brief %Set all elements of a matrix to zero.
* \param[in] m Vector to initialise
* \param[in] rows Number of columns
* \param[in] cols Number of rows
*/
void zero_matrix(MATRIX_T m, int rows, int cols);

/*!
* \brief	Free a matrix
* \details	De-allocate the memory assigned to a matrix.
*			The function create_matrix() allocates memory as a contiguous block,
*			so just need a pointer to the matrix.
*			After deallocation, the value of the pointer is set to NULL.
* \param[in,out] *arr Pointer to matrix which needs freeing
*/
void free_matrix(MATRIX_T *arr);

/*!
* \brief	%Set all elements of vector to a specific value.
* \param[in,out] v Vector to initialise
* \param[in] cols Size of the vector
* \param[in] value Value to assign to all elements of the vector
*/
void set_vector(VECTOR_T v, int cols, double value);

/*!
* \brief	Create a output file name based on 'run id', processor rank etc...
* \details	Concatonates the parameter elements to single string.
*			Simply call to sprintf(). Be careful with allocating appropriate buffer length
*			as this method does not do any bounds checking.<br>
*			The last character of dir should be a forward or back slash.
* \bug		Forgot to add length checking to this method so make sure buffers are correctly assigned.
* \param[in, out] buffer Place to bung the file name
* \param[in] dir A directory path
* \param[in] runId A cluster run identifier code
* \param[in] rank The processor rank (normally 0 to N-1 cluster size)
* \param[in] suffix Suffix of the output file
* \see suffixes.h
*/
void set_datafile_path(char *buffer, char *dir, char *runId, int rank, char *suffix);

/*!
* \brief	Read the number of processors associated with a statistical output file.
* \details	When a <em>permuted</em> statistical calculation is run on the NIBHI cluster, 
			the results are dumped typically to a text output file. 
			These result files are processed to combine the data with the non-permuted calculation. 
			If 10 cores are associated with a calculation, your looking 10 result files.
			This number is read from the file as the first integer read from a text file.
			Output file names are created from the a working directory path, run id, the processor rank and a suffix.
			This function searchs each file in turn until a number greater than zero is found.
* \bug Method does no initialisation checking (need to update method).
* \param[in,out] dst A buffer where the filepath generated is copied to.
* \param[in,out] line Buffer to place the line of text read from a file.
* \param[in] line_length Length of the 'text' line buffer.
* \param[in] dir The working directory path
* \param[in] runId A cluster run identifier code
* \param[in] suffix Suffix of the output file
* \returns integer of the number of cores associated with a calculation (return zero on failure)
* \see suffixes.h
*/
int readNumberOfProcs(char *dst, char *line, int line_length, char *dir, char *runId, char *suffix);

/*!
* \brief	Dump a text file into a 2-D character array.
* \details  Dumps an entire text file into a string array.
*			The text file is scanned once to get the maximum line width and the number of lines.
*			Storage is allocated, the file is re-read and copied to the buffer.
*			Do not use for massive files, designed for looking at the smaller 'sample' files, 
*			i.e. a few thousand lines of text.
* \param[in] filepath Text file to read
* \param[in,out] nLines Pointer where the number of lines is stored (set to zero on failure).
* \returns 2-D character array of the text file (returns NULL on failure)
*/
char** load_file(char *filepath, int *nLines);

/*!
* \brief	Create a 2-D char array
* \details  Create a 2-D char array of specified dimensions.
			Allocated strings are initialised to be zero-length, NULL terminated.
			Free by calling free_2d_char_array().
* \param[in] nRows No. strings to allocate
* \param[in] row_length Maximum string width
* \returns 2-D array of char (returns NULL on failure)
* \see free_2d_char_array()
*/
char** create_2d_char_array(size_t nRows, size_t row_length);

/*!
* \brief	Get the number of tokens in a string
* \details	If passed a line of text and a delimiter character, parses the line to get
*			the number of tokens. A token is defined as a contigious block of alphanumeric characters.
*			The string must be NULL terminated. Tokenisation done on the delimiter character.
*			Zero-length tokens do not contribute to the token count.
*			Whitespace is ignored by default. Method is clunky and assumes the maximum token size is
*			80 characters.
* \param[in] str Line of text 
* \param[in] delimiter Delimiter character (typically comma or space)
* \returns No. tokens in the line of text
*/
int get_number_of_tokens(char *str, char delimiter);

/*!
* \brief	Get a token from a line of text.
* \details	If passed a line of text and a delimiter character, parses the line to get
*			a token at a particular index which is copied into the destination array.
*			Tokens are NULL terminated as should be the source string.
* \param[in] src Line of text/string
* \param[in] dst Destination buffer for the token
* \param[in] index Token index (0 to N-1 tokens)
* \param[in] delimiter Delimiter character for the line of text
* \returns return TRUE on success
*/
BOOL get_token(char *src, char *dst, int index, char delimiter);

/*! 
* \brief	Get a 'token' column from a 2-D character array storing an entire text file.
* \deprecated Now using the 'filtering' aware method found in solfile.h
* \details  The lines in the array are tokenised and the results placed in the pre-allocated
*			destination buffer. Will ignore a header line as appropriate.
*			Should not be used for really big files.
*			All string must be NULL-terminated.
* \param[in] src line of text to read a delimited column from
* \param[in,out] dst Destination buffer for the tokens read from the text file.
* \param[in] nRows No. rows/lines in the source buffer
* \param[in] index Index of token in the line (0 to N-1, N = No. tokens in line)
* \param[in] hasHeader %Set to TRUE to skip header line.
* \param[in] delimiter Delimiter character to tokenise on
* \returns TRUE on success
*/
BOOL get_column(char **src, char **dst, int nRows, int index, BOOL hasHeader, char delimiter);

/*!
 * \brief Initialise 2-D array of char to NULL-terminated, zero-length strings.
 */
void zero_2d_char_array(char **src, int nRows);

/*!
 * \brief		Get the levels in a 2-D char array
 * \deprecated	Using the filter-aware levelling code specified in solfile.h
 * \details		Get the levels (i.e. unique string list) in a 2-D char array.
 *				The generated list is placed in the destination buffer.
 *				Specify if allow empty string as valid token.
 *				Levels permitted up to maximum size of the destination array,
 *				if this limit is exceeded likely the source array contains a continous variable 
 *				and the method fails.
 * \param[in] src src List of string to get the levels
 * \param[in] nSrc Size of the source string array
 * \param[in,out] dst Pre-allocated destination buffer for the level strings
 * \param[in] nDst Size of the destination array
 * \param[in,out] nLevels Pointer where the number of levels found is written to (set to zero on failure)
 * \param[in] allowEmptyStrings %Set to TRUE to allow zero-length string to be counted as a level
 * \returns TRUE on success
 */
BOOL get_levels(char **src, int nSrc, char **dst, int nDst, int *nLevels, BOOL allowEmptyStrings);

/*!
* \brief	QSORT comparator for 2 strings
* \details	String ascending-comparison method for the quick-sort algorithm.
			Strings must be NULL-terminated.
* \param a A NULL-terminated string
* \param b A NULL-terminated string
*/
int cstring_cmp(const void *a, const void *b);

/*!
* \brief	Free a 2-D char array
* \details	De-allocate the contents of the source array.
* \bug		Once freed, this method does not set the source array to NULL.
* \param[in,out] src 2-D array of char to free
* \param[in] nRows Size of the source array
* \return 1 on success
* \see create_2d_char_array()
*/
int free_2d_char_array(char **src, size_t nRows);

/*!
* \brief %Set all elements of a integer array to zero.
*/
void zero_int_array(int *src, int nSrc);

/*!
* \brief Echo integer array to STDOUT
*/
void echo_int_array(int *src, int nSrc);

/*!
* \brief	Create 2-D integer array
* \details	Create 2-D intger array of specified dimensions with each element assigned 
*			an initial value. Memory assigned as a contigious block sub-divided 
*			by a pointer array.
* \param[in] rows Rows to allocate
* \param[in] max_width Columns to allocate
* \param[in] initial_value Value assign to all array elements.
* \returns 2-D integer array of specified size (returns NULL on failure)
* \see free_2d_int_array()
*/
int** create_2d_int_array(int rows, int max_width, int initial_value);

/*!
* \brief	Free a 2-D integer array
* \details	De-allocate the contents of the source array.
* \bug		Once freed, this method does not set the source array to NULL.
* \param[in,out] src 2-D array of char to free
* \param[in] nRows Size of the source array
* \see create_int_char_array()
*/
void free_2d_int_array(int **src, int nRows);

/*!
* \brief	Echo string array to STDOUT
* \param[in] src string array
* \param[in] nRows No. strings
*/
void echo_2d_char_array(char **src, int nRows);

/*!
* \brief	Echo matrix to STDOUT
* \param[in] u The matrix
* \param[in] nRows No. rows in matrix
* \param[in] nCols No. columns in matrix
*/
void echo_matrix(MATRIX_T u, int nRows, int nCols);

/*!
* \brief	Echo vector to STDOUT
* \param[in] u The vector
* \param[in] nCols No. elements in the vector
*/
void echo_vector(VECTOR_T u, int nCols);

/*!
* \brief Initialise elements of integer array to specific value
* \param[in,out] src Integer array
* \param[in] size Size of the source array
* \param[in] value Initial value
*/
void init_int_array(int *src, int size, int value);

/*!
* \brief Ascending order comparison function for QSORT
* \param[in] a Pointer to an integer
* \param[in] b Pointer to an integer
*/
int int_cmp(const void *a, const void *b);

/*!
* \brief		Read a matrix from a text file.
* \deprecated	Not used
* \details		2 integers on first line specified the matrix dimensions
* \param[in]    file Path to the input matrix file
* \param[in,out] nRows No. rows of matrix assigned here
* \param[in,out] nCols No. columns of matrix assigned here
* \returns		A matrix (NULL on failure)
*/
MATRIX_T read_matrix(char *file, int *nRows, int *nCols);

/*! 
* \brief Initialise a BOOL array to specified value
* \param[in,out] src Source array
* \param[in] size Size of source array
* \param[in] value Initial value
*/
void init_bool_array(BOOL *src, int size, BOOL value);

/*!
* \brief Read an array of integer from a text file.
* \details 1 integer per line, fscanf() read.
* \param[in] filepath Path to the text file
* \param[in,out] size No. of integers read assigned here (set to zero on failure)
* \returns integer array (or NULL on failure)
*/
int* read_int_array(char* filepath, int *size);

/*!
* \brief Create 2-D array of BOOL
* \param[in] rows No. rows to allocate
* \param[in] columns No. columns to allocate
* \param[in] initial_value Initialisation value for the array
* \returns 2-D array of BOOL (or NULL on failure)
* \see free_2d_bool_array()
*/
BOOL** create_2d_bool_array(int rows, int columns, BOOL initial_value);

/*!
* \brief	Free 2-D array of BOOL
* \details	create_2d_bool_array() assigns memory as contigious block so
*			just need a pointer to the array.
* \bug		Once freed, this method does not set the source array to NULL.
* \param[in,out] arr The array to de-allocate
*/
void free_2d_bool_array(BOOL **arr);

/*!
* \brief Fill indices of integer array (size=N) with series of 0 to N-1.
* \param[in,out] src Source array
* \param[in] nSrc Size of the source array.
*/
void fill_indices(int *src, int nSrc);

/*! 
* \brief Create a string array of specified size
* \param[in] size Size to allocate
* \param[in] initial_value Initial Value for each element in the string
* \returns string (or NULL on failure)
*/
char* create_char_array(int size, char initial_value);

/*! 
* \brief Echo BOOL array to STDOUT.
* \details TRUE = 1, FALSE = 0
* \param[in] src Source array
* \param[in] nSrc Size of source array
*/
void echo_bool_array(BOOL *src, int nSrc);

/*!
* \brief %Set all strings in array to lower case
* \param[in,out] src Source array
* \param[in] nRows Size of source array
*/
int set_all_lowercase(char **src, int nRows);

/*!
* \brief Ascending order comparison function for QSORT
* \param[in] a Pointer to a double
* \param[in] b Pointer to a double
*/
int double_cmp(const void *a, const void *b);

#ifdef __cplusplus
}
#endif

#endif // _UTILS_H_
